import parseKeys from "../parse-asn1";
import randomBytes from "../randombytes";
import createHash from "../create-hash";
import mgf from "./mgf";
import xor from "./xor";
import BN from "../bn.js";
import withPublic from "./withPublic";
import crt from "../browserify-rsa";
function oaep(key, msg) {
    var k = key.modulus.byteLength();
    var mLen = msg.length;
    var iHash = createHash('sha1').update(Buffer.alloc(0)).digest();
    var hLen = iHash.length;
    var hLen2 = 2 * hLen;
    if (mLen > k - hLen2 - 2) {
        throw new Error('message too long');
    }
    var ps = Buffer.alloc(k - mLen - hLen2 - 2);
    var dblen = k - hLen - 1;
    var seed = randomBytes(hLen);
    var maskedDb = xor(Buffer.concat([iHash, ps, Buffer.alloc(1, 1), msg], dblen), mgf(seed, dblen));
    var maskedSeed = xor(seed, mgf(maskedDb, hLen));
    return new BN(Buffer.concat([Buffer.alloc(1), maskedSeed, maskedDb], k));
}
function pkcs1(key, msg, reverse) {
    var mLen = msg.length;
    var k = key.modulus.byteLength();
    if (mLen > k - 11) {
        throw new Error('message too long');
    }
    var ps;
    if (reverse) {
        ps = Buffer.alloc(k - mLen - 3, 0xff);
    }
    else {
        ps = nonZero(k - mLen - 3);
    }
    return new BN(Buffer.concat([Buffer.from([0, reverse ? 1 : 2]), ps, Buffer.alloc(1), msg], k));
}
function nonZero(len) {
    var out = Buffer.allocUnsafe(len);
    var i = 0;
    var cache = randomBytes(len * 2);
    var cur = 0;
    var num;
    while (i < len) {
        if (cur === cache.length) {
            cache = randomBytes(len * 2);
            cur = 0;
        }
        num = cache[cur++];
        if (num) {
            out[i++] = num;
        }
    }
    return out;
}
export default (function publicEncrypt(publicKey, msg, reverse) {
    var padding;
    if (publicKey.padding) {
        padding = publicKey.padding;
    }
    else if (reverse) {
        padding = 1;
    }
    else {
        padding = 4;
    }
    var key = parseKeys(publicKey);
    var paddedMsg;
    if (padding === 4) {
        paddedMsg = oaep(key, msg);
    }
    else if (padding === 1) {
        paddedMsg = pkcs1(key, msg, reverse);
    }
    else if (padding === 3) {
        paddedMsg = new BN(msg);
        if (paddedMsg.cmp(key.modulus) >= 0) {
            throw new Error('data too long for modulus');
        }
    }
    else {
        throw new Error('unknown padding');
    }
    if (reverse) {
        return crt(paddedMsg, key);
    }
    else {
        return withPublic(paddedMsg, key);
    }
});
